<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0,viewport-fit=cover"><title>Vercel部署 | 耶比大熊</title><meta name="author" content="耶比大熊,2324308799@qq.com"><meta name="copyright" content="耶比大熊"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="ffffff"><meta name="description" content="关于 vercelVercel 是一个云服务平台，支持静态网站和动态网站的应用部署、预览和上线。如果你用过 GitHub Pages ，那么心里可能不会太陌生，但你也能通过 vercel 集成 GitHub 后后，在 GitHub 项目进行代码推送，PR合并自动部署的目的，且你不需要考虑服务器问题。 与其说这是 vercel 可行性调研方案，不如说是 vercel 用法普及，因为 vercel">
<meta property="og:type" content="article">
<meta property="og:title" content="Vercel部署">
<meta property="og:url" content="https://cxzde.cn/2024/04/09/note-vercel/index.html">
<meta property="og:site_name" content="耶比大熊">
<meta property="og:description" content="关于 vercelVercel 是一个云服务平台，支持静态网站和动态网站的应用部署、预览和上线。如果你用过 GitHub Pages ，那么心里可能不会太陌生，但你也能通过 vercel 集成 GitHub 后后，在 GitHub 项目进行代码推送，PR合并自动部署的目的，且你不需要考虑服务器问题。 与其说这是 vercel 可行性调研方案，不如说是 vercel 用法普及，因为 vercel">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://tuchuang.voooe.cn/images/2024/04/07/background4.jpg">
<meta property="article:published_time" content="2024-04-09T09:00:00.000Z">
<meta property="article:modified_time" content="2024-04-09T09:00:00.000Z">
<meta property="article:author" content="耶比大熊">
<meta property="article:tag" content="blog">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://tuchuang.voooe.cn/images/2024/04/07/background4.jpg"><link rel="shortcut icon" href="/favicon.ico"><link rel="canonical" href="https://cxzde.cn/2024/04/09/note-vercel/index.html"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css?v=4.14.0-b1"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5.1/css/all.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-snackbar@0.1.16/dist/snackbar.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui@5.0.35/dist/fancybox/fancybox.min.css" media="print" onload="this.media='all'"><script>(()=>{
      const saveToLocal = {
        set: (key, value, ttl) => {
          if (ttl === 0) return
          const now = Date.now()
          const expiry = now + ttl * 86400000
          const item = {
            value,
            expiry
          }
          localStorage.setItem(key, JSON.stringify(item))
        },
      
        get: key => {
          const itemStr = localStorage.getItem(key)
      
          if (!itemStr) {
            return undefined
          }
          const item = JSON.parse(itemStr)
          const now = Date.now()
      
          if (now > item.expiry) {
            localStorage.removeItem(key)
            return undefined
          }
          return item.value
        }
      }
      
      window.btf = {
        saveToLocal: saveToLocal,
        getScript: (url, attr = {}) => new Promise((resolve, reject) => {
          const script = document.createElement('script')
          script.src = url
          script.async = true
          script.onerror = reject
          script.onload = script.onreadystatechange = function() {
            const loadState = this.readyState
            if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
            script.onload = script.onreadystatechange = null
            resolve()
          }

          Object.keys(attr).forEach(key => {
            script.setAttribute(key, attr[key])
          })

          document.head.appendChild(script)
        }),

        getCSS: (url, id = false) => new Promise((resolve, reject) => {
          const link = document.createElement('link')
          link.rel = 'stylesheet'
          link.href = url
          if (id) link.id = id
          link.onerror = reject
          link.onload = link.onreadystatechange = function() {
            const loadState = this.readyState
            if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
            link.onload = link.onreadystatechange = null
            resolve()
          }
          document.head.appendChild(link)
        }),

        addGlobalFn: (key, fn, name = false, parent = window) => {
          const pjaxEnable = false
          if (!pjaxEnable && key.startsWith('pjax')) return

          const globalFn = parent.globalFn || {}
          const keyObj = globalFn[key] || {}
    
          if (name && keyObj[name]) return
    
          name = name || Object.keys(keyObj).length
          keyObj[name] = fn
          globalFn[key] = keyObj
          parent.globalFn = globalFn
        }
      }
    
      const activateDarkMode = () => {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      const activateLightMode = () => {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', 'ffffff')
        }
      }

      btf.activateDarkMode = activateDarkMode
      btf.activateLightMode = activateLightMode
      
      const t = saveToLocal.get('theme')
    
          const now = new Date()
          const hour = now.getHours()
          const isNight = hour <= 6 || hour >= 18
          if (t === undefined) isNight ? activateDarkMode() : activateLightMode()
          else if (t === 'light') activateLightMode()
          else activateDarkMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
      const detectApple = () => {
        if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
          document.documentElement.classList.add('apple')
        }
      }
      detectApple()
    })()</script><script>const GLOBAL_CONFIG = {
  root: '/',
  algolia: undefined,
  localSearch: {"path":"/search.xml","preload":false,"top_n_per_article":1,"unescape":false,"languages":{"hits_empty":"找不到您查询的内容：${query}","hits_stats":"共找到 ${hits} 篇文章"}},
  translate: {"defaultEncoding":2,"translateDelay":0,"msgToTraditionalChinese":"繁","msgToSimplifiedChinese":"简"},
  noticeOutdate: {"limitDay":365,"position":"top","messagePrev":"It has been","messageNext":"days since the last update, the content of the article may be outdated."},
  highlight: {"plugin":"highlight.js","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":230},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: true,
    post: true
  },
  runtime: '天',
  dateSuffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: {"chs_to_cht":"你已切换为繁体中文","cht_to_chs":"你已切换为简体中文","day_to_night":"你已切换为深色模式","night_to_day":"你已切换为浅色模式","bgLight":"#49b1f5","bgDark":"#1f1f1f","position":"top-right"},
  infinitegrid: {
    js: 'https://cdn.jsdelivr.net/npm/@egjs/infinitegrid@4.11.1/dist/infinitegrid.min.js',
    buttonText: '加载更多'
  },
  isPhotoFigcaption: false,
  islazyload: true,
  isAnchor: false,
  percent: {
    toc: false,
    rightside: false,
  },
  autoDarkmode: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'Vercel部署',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2024-04-09 17:00:00'
}</script><link rel="stylesheet" href="/css/mouse.css"><link rel="stylesheet" href="/css/iconfont/iconfont.css"><meta name="generator" content="Hexo 7.1.1"></head><body><div id="loading-box"><div class="loading-left-bg"></div><div class="loading-right-bg"></div><div class="spinner-box"><div class="configure-border-1"><div class="configure-core"></div></div><div class="configure-border-2"><div class="configure-core"></div></div><div class="loading-word">加载中...</div></div></div><script>(()=>{
  const $loadingBox = document.getElementById('loading-box')
  const $body = document.body
  const preloader = {
    endLoading: () => {
      $body.style.overflow = ''
      $loadingBox.classList.add('loaded')
    },
    initLoading: () => {
      $body.style.overflow = 'hidden'
      $loadingBox.classList.remove('loaded')
    }
  }

  preloader.initLoading()
  window.addEventListener('load',() => { preloader.endLoading() })

  if (false) {
    btf.addGlobalFn('pjaxSend', () => { preloader.initLoading() }, 'preloader_init')
    btf.addGlobalFn('pjaxComplete', () => { preloader.endLoading() }, 'preloader_end')
  }
})()</script><div id="web_bg"></div><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src= "" data-lazy-src="/img/avatar.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">10</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">0</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">0</div></a></div><hr class="custom-hr"/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="not-top-img" id="page-header"><nav id="nav"><span id="blog-info"><a href="/" title="耶比大熊"><span class="site-name">耶比大熊</span></a></span><div id="menus"><div id="search-button"><span class="site-page social-icon search"><i class="fas fa-search fa-fw"></i><span> 搜索</span></span></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div><div id="toggle-menu"><span class="site-page"><i class="fas fa-bars fa-fw"></i></span></div></div></nav></header><main class="layout" id="content-inner"><div id="post"><div id="post-info"><h1 class="post-title">Vercel部署</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2024-04-09T09:00:00.000Z" title="发表于 2024-04-09 17:00:00">2024-04-09</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2024-04-09T09:00:00.000Z" title="更新于 2024-04-09 17:00:00">2024-04-09</time></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">3.8k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>11分钟</span></span><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="Vercel部署"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div><article class="post-content" id="article-container"><hr>
<h1 id="关于-vercel"><a href="#关于-vercel" class="headerlink" title="关于 vercel"></a>关于 vercel</h1><p>Vercel 是一个云服务平台，支持静态网站和动态网站的应用部署、预览和上线。如果你用过 <code>GitHub Pages</code> ，那么心里可能不会太陌生，但你也能通过 vercel 集成 GitHub 后后，在 GitHub 项目进行代码推送，PR合并自动部署的目的，且你不需要考虑服务器问题。</p>
<p>与其说这是 vercel 可行性调研方案，不如说是 vercel 用法普及，因为 vercel 自身内置 CI CD，只要你将项目与 vercel 关联，你就能通过命令快速部署，这没什么难度，接下来给大家讲解 vercel 优势、部署方式、能力边界以及接下来工作可能要思考的点。</p>
<h1 id="Vercel-优势（能带来什么）"><a href="#Vercel-优势（能带来什么）" class="headerlink" title="Vercel 优势（能带来什么）"></a>Vercel 优势（能带来什么）</h1><p>使用 vercel 能为我们带来什么？关于 vercel 能力优势，这里我根据我个人使用体验简单罗列：</p>
<ul>
<li>个人版永久免费，每个月 100G 带宽（别人访问你的项目所耗费的流量），个人项目部署完全够用，需要注意的是团队模式收费，所以要协作你只能付费。</li>
<li>内置 CI CD，你可以理解成一个黑盒，项目丢进去，只需要将项目导入 vercel ，一句命令自动部署。</li>
<li>因为内置构建流程，支持代码推送、PR 自动触发构建，不同分支唯一地址，方便测试。</li>
<li>支持本地、测试、生产三种环境部署，仅仅是命令区别，上手成本极低。</li>
<li>丰富的集成能力，项目部署自动监控，端到端自动化测试等等，当然这些并并属于 vercel 自身的能力，但它可以为你提供集成入口，让这些成为你自动部署中自动进行的一步，比如构建生产后自动完成性能指标输出，自动化测试，以及后续项目监控等等。</li>
</ul>
<hr>
<h1 id="部署方式"><a href="#部署方式" class="headerlink" title="部署方式"></a>部署方式</h1><p>将项目导入 vercel 后，vercel 会自动检测并为项目所用的框架设置最佳构建配置和部署配置，这也是为什么导入项目就能直接构建的一部分原因，我们先说两种导入项目到 vercel 平台的两种方式。</p>
<h2 id="GitHub部署"><a href="#GitHub部署" class="headerlink" title="GitHub部署"></a>GitHub部署</h2><p>在 dashboard 点击 <code>Add New</code> 按钮选择 <code>Project</code>，这时会跳转到导入项目界面，选择你的 GitHub 账号（如果之前没绑定这里也可以绑定 GitHub 账号），然后点击导入按钮，这是会跳转到项目设置。这些配置在此时都可以根据实际情况修改。</p>
<p>直接点击下面的 Deploy 进行部署，然后坐等构建就能看到 vercel 为我们部署后独一无二的预览地址了。</p>
<p>由于实际开发，我们本地肯定也会克隆 GitHub 项目，那么在后续只要我们修改项目代码，push 到仓库，GitHub 感知到代码变动，vercel 就会自动再次部署。</p>
<p>你可能会想，我 GitHub 项目拷贝下来会有主分支，以及我的开发分支，vercel 虽然能自动部署，它怎么知道我要部署到什么环境，事实上 vercel 也已经做了提前预设，假设你的代码变动发生在 <code>main</code> 或者 <code>master</code> 分支，那么 vercel 就会自动构建部署生产环境，除此之外的分支，vercel 都会更新预览（测试）环境。</p>
<p>关于 GitHub 主分支，你可以在 GitHub 进行设置，这个 vercel 无需感知，反正是主分支变动它会对应帮你部署就完事了。</p>
<p>另外，让远程仓库代码变动有两种方式，第一种是直接本地推送代码，第二种就是提 PR 后合并到目标分支触发变动。</p>
<p>假设你集成了 GitHub ，在你 PR 合并前，你甚至能在 GitHub PR 中直接看到你需要提 PR 的分支部署后的预览地址，直接看 vercel 帮你部署后的效果。</p>
<p><img src= "" data-lazy-src="https://cdn.jsdelivr.net/gh/cxz206015/For-PicGo@main/img/1213309-20230703213817714-707002200.png" alt="img"></p>
<p>上图就是我基于主分支 <code>main</code> 切出了一个开发分支 <code>main-echo</code>，然后给 <code>main</code> 分支提了一个 PR，在 PR 合并页面你能看到 GitHub 这里直接给出了 <code>main-echo</code> 分支的部署预览地址，之后我们合并 PR ，由于 <code>main</code> 分支代码发生改变，vercel 会自动帮你构建部署 <code>main</code> 分支，好处就是能保证每次部署前，你能直接看每个分支效果是否符合你的预期。</p>
<p><img src= "" data-lazy-src="https://cdn.jsdelivr.net/gh/cxz206015/For-PicGo@main/img/1213309-20230703213901286-2021907469.png" alt="img"></p>
<p>可以看到 PR 合并后，vercel 这边已经帮我更新了生产环境，且提交记录与 GitHub 保持一致。</p>
<h2 id="本地部署"><a href="#本地部署" class="headerlink" title="本地部署"></a>本地部署</h2><p>有时候，我们的项目可能并不在 GitHub 上，我如果需要上传 GitHub 还要做初始化一系列操作，比较麻烦，那么我们就可以通过本地初始化 vercel 让项目直接关联到 vercel，之后直接通过构建命令来达到不同环境的部署效果。</p>
<p>我们需要在本地安装 vercel，建议是全局安装：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -g vercel</span><br></pre></td></tr></table></figure>

<p>之后执行如下命令保证你本地登录了 vercel 账号：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vercel login</span><br></pre></td></tr></table></figure>

<p>之后进入你的项目根路径，执行 vercel 即可：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vercel</span><br></pre></td></tr></table></figure>

<p>之后你需要进行一些基础配置，比如你需要选择要部署的项目目录、构建命令和输出目录，如果直接回车就是执行默认配置，这些配置走完后项目就会进行部署，你可以直接等待终端部署完成后的预览地址，你可以回到 vercel 后台找到你刚才关联部署的项目，一样可以找到预览地址。</p>
<p>我们需要区分 GitHub 部署与本地部署的一些区别，集成 GitHub 后，vercel 会根据 GitHub 代码仓库分支变化，对应的自动部署匹配环境：</p>
<ul>
<li>生产环境：假设 GitHub 主分支直接发生代码变动，比如 push，或者有 PR 合并到主分支都会导致生产环境重新部署，而主分支默认是 main 或者 master 分支，你可以在 GitHub 自定义你的主分支。</li>
<li>预览环境：除了主分支之外的其他分支如果发生代码变动，vercel 会自动构建属于这个分支的独一无二的预览地址。</li>
</ul>
<p>我们也可以直接通过命令来进行本地部署，但如果我们集成 GitHub，还是建议走 GitHub 工作流，这样构建会更加规范。</p>
<p>因此构建命令更适用于本地部署，因为我们的项目并未集成 GitHub ，vercel 无处感知代码变化，这就需要开发来手动通过命令来达到不同环境部署更新的目的，接下来我们介绍不同环境的区别以及对应的命令。</p>
<hr>
<h1 id="构建命令与环境介绍"><a href="#构建命令与环境介绍" class="headerlink" title="构建命令与环境介绍"></a>构建命令与环境介绍</h1><p>vercel 其实也分为开发环境，预览环境（测试环境）以及生产环境三个概念，在 vercel 团队版，你甚至能在预览环境直接进行评论（而且评论也能集成到 slack，挺符合目前我们的生态），比如 UI 觉得某些页面还原度不够他就能在预览环境进行评论，所以不同环境确实有本质上的区别以及作用。</p>
<p>我们可以通过如下三个命令分别构建不同环境，因为比较简单，就一笔带过：</p>
<ol>
<li><p><code>vercel dev</code>： 这个命令用于启动本地的开发环境。它会模拟 Vercel 的云环境，让你可以在本地进行开发和测试。使用这个命令，你可以实时看到你的更改效果，而不需要将它们部署到预览或生产环境。</p>
<p><img src= "" data-lazy-src="https://cdn.jsdelivr.net/gh/cxz206015/For-PicGo@main/img/1213309-20230703213914338-777779399.png" alt="img"></p>
</li>
<li><p><code>vercel</code>： 这个命令用于将你的项目部署到 Vercel 的预览环境。预览环境是一个为了测试和分享而设立的临时环境，你可以在里面看到你的更改会在生产环境中出现的样子。这个命令非常适用于团队合作的场景，你可以用它来分享你的更改，获取反馈，然后在推向生产环境之前进行进一步的调整。</p>
</li>
<li><p><code>vercel --prod</code>： 这个命令会将你的应用部署到生产环境。生产环境通常代表了你的应用的正式发布版本，所部署的内容会对公众可见。这个命令就是将你的项目部署上线的最终步骤。</p>
</li>
</ol>
<hr>
<h1 id="踩坑与-vercel-平台设置"><a href="#踩坑与-vercel-平台设置" class="headerlink" title="踩坑与 vercel 平台设置"></a>踩坑与 vercel 平台设置</h1><p>单论 vercel 平台你会发现非常简单，导入项目，通过代码推送或者几句命令基本上能满足你的大部分需求，但事实上，我觉得困难的其实是将你个人的项目导入 vercel 并部署成功。</p>
<p>假设你使用 vercel 官方各框架的模版，因为这些模版比较纯粹简单，你确实不需要任何改动就能成功，但假设你是一个开发已久包含各种定制的项目，可能就没那么顺利，接下来介绍下我将个人项目部署到 vercel 所遇到的一些问题。</p>
<ul>
<li><p>vercel 部署对于文件大小写引用更为严格，比如我发现自己项目一些文件引用文件是小写，但引用某个字母大写，Jenkins部署这些非常正常，也不会报错，但vercel 会认为这是错误，并直接报错，如下图。</p>
<p><img src= "" data-lazy-src="https://cdn.jsdelivr.net/gh/cxz206015/For-PicGo@main/img/1213309-20230703213921722-2065595973.png" alt="img"></p>
<p>这里其实就是因为实际的文件命为 <code>checkbox</code> 而非 <code>Checkbox</code>，所以如果遇到这样的错误，直接找到构建错误信息的文件去看看资源是否存在，或者资源命是否大小写匹配。</p>
</li>
<li><p>vercel 在未来只支持 node 18 版本，且 vercel 平台默认就是 18 版本，一般如果你遇到了如下错误，那就是说明 node 版本过高，比如我们现在的项目使用的版本就是 16 ，然后走 vercel 部署 使用18 版本就会报错。</p>
<p><img src= "" data-lazy-src="https://cdn.jsdelivr.net/gh/cxz206015/For-PicGo@main/img/1213309-20230703213929296-1416631248.png" alt="img"></p>
<p>我之前一直以为需要降低我本地的 node 版本，后来一想不对啊，vercel 部署走的是它的服务，跟我本地环境有啥关系，果然在项目设置中我们能找到 node 版本设置，将其修改为与项目 node 版本匹配的版本之后再构建就不会有上图的错误。</p>
<p><img src= "" data-lazy-src="https://cdn.jsdelivr.net/gh/cxz206015/For-PicGo@main/img/1213309-20230703213940932-1652831555.png" alt="img"></p>
<p>但即便改成 16 避免了这个报错，vercel 还是会给出 node 版本的告警，它会告诉你在今年8月不再支持 node 14 以及 16 版本的部署，以及在官方博客也提到了这个问题。</p>
<p><img src= "" data-lazy-src="https://cdn.jsdelivr.net/gh/cxz206015/For-PicGo@main/img/1213309-20230703213947890-486521130.png" alt="img"></p>
<p><img src= "" data-lazy-src="https://cdn.jsdelivr.net/gh/cxz206015/For-PicGo@main/img/1213309-20230703213959186-1250984255.jpg" alt="img"></p>
</li>
<li><p>你可能需要根据自身项目构建配置，对应的调整 vercel 平台的项目设置，比如在这里可以看到构建相关设置，在之前我们提到 vercel 会根据你的项目自动识别框架并初始化默认配置，比如我们项目的在上传后 vercel 成功识别成了 umi，且默认输出目录是 <code>dist</code>，但事实上我们的输出目录并不是这个名字，所以需要在这里修改。</p>
<p><img src= "" data-lazy-src="https://cdn.jsdelivr.net/gh/cxz206015/For-PicGo@main/img/1213309-20230703214005577-100434401.jpg" alt="img"></p>
</li>
<li><p>vercel 平台环境并不支持 zip，我在很长一段时间构建项目时 vercel 一直给我报找不到 zip 命令的错误，但我并未发现我的包依赖中有这个包名，同时保险起见，我甚至专门在项目中安装了 zip 这个包，但结果依旧不行，之后我尝试询问 GPT，也确实在 build 命令中发现了一小段利用 zip 来压缩打包产物的命令，在删除这段行为后构建正常。</p>
<p><img src= "" data-lazy-src="https://cdn.jsdelivr.net/gh/cxz206015/For-PicGo@main/img/1213309-20230703214011578-244828207.png" alt="img"></p>
<p>另外，如果大家看到类似上面的报错，一定不要去管两个错误，比如 <code>exited with 1</code>这种，这类错误都是属于构建失败的通用错误，就像接口 500 给了你一个通用 code 码一样，真正的错误一定就是第一句，你只用解决这个错误就完事了。</p>
</li>
</ul>
<hr>
<h1 id="能力边界"><a href="#能力边界" class="headerlink" title="能力边界"></a>能力边界</h1><p>以上是 vercel 平台部署以及一些使用上需要注意的点，考虑到未来项目发展，这里顺带普及下我注意到的能力边界。</p>
<h2 id="支持域名自定义"><a href="#支持域名自定义" class="headerlink" title="支持域名自定义"></a>支持域名自定义</h2><p>理论 vercel 在项目部署后会给我们生产独一无二的地址，但都带有 vercel 后缀，如果是生产环境对外，vercel 痕迹就太明显了，所以如果大家有购买域名，也可以在项目设置中自定义域名。</p>
<p><img src= "" data-lazy-src="https://cdn.jsdelivr.net/gh/cxz206015/For-PicGo@main/img/1213309-20230703214019219-878397012.png" alt="img"></p>
<h2 id="支持部署-URL-私有化与预览地址自定义（要钱）"><a href="#支持部署-URL-私有化与预览地址自定义（要钱）" class="headerlink" title="支持部署 URL 私有化与预览地址自定义（要钱）"></a>支持部署 URL 私有化与预览地址自定义（要钱）</h2><p>当我们部署预览环境或生产环境时，Vercel 将自动生成唯一的 URL 便于我们访问；默认情况下，该 URL 可以公开访问，但我们可以使用部署保护将其配置为私有地址，比如我们并不希望外界访问我们的测试地址。</p>
<p>关于 URL 这里先介绍几种地址结构：</p>
<p>生成环境生成的地址一般是项目名拼接 scope，结构为：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;project-name&gt;-&lt;scope-slug&gt;.<span class="property">vercel</span>.<span class="property">app</span>;</span><br></pre></td></tr></table></figure>

<p>除此之外还有项目名拼项目唯一哈希的地址：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;project-name&gt;-&lt;unique-hash&gt;-&lt;scope-slug&gt;.<span class="property">vercel</span>.<span class="property">app</span></span><br></pre></td></tr></table></figure>

<p>当然有时候我们需要区分项目，所以 vercel 也提供了拼接了项目分支的地址：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;project-name&gt;-git-&lt;branch-name&gt;-&lt;scope-slug&gt;.<span class="property">vercel</span>.<span class="property">app</span></span><br></pre></td></tr></table></figure>

<p>除了分支，假设我们是团队版，vercel 还会提供项目名拼接用户名的地址，便于区分这个地址是谁构建，结构为：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;project-name&gt;-&lt;author-name&gt;-&lt;scope-slug&gt;.<span class="property">vercel</span>.<span class="property">app</span>;</span><br></pre></td></tr></table></figure>

<p>我们在项目部署面板可以看到不同类型的地址类型，当然这些地址最终呈现的效果完全一致（这里我是个人版）</p>
<p><img src= "" data-lazy-src="https://cdn.jsdelivr.net/gh/cxz206015/For-PicGo@main/img/1213309-20230703214030336-1621190236.png" alt="img"></p>
<p>聊聊部署保护，vercel 支持单个部署添加密码保护或者直接将部署设置为私有化部署，前者需要输入预设的密码才能访问地址，后者可以直接大范围让所有部署地址变成私有化，你可能需要密码或者 vercel 身份效验才能访问，比如企业版中我们可能不希望团队外任何人访问测试地址。</p>
<p><img src= "" data-lazy-src="https://cdn.jsdelivr.net/gh/cxz206015/For-PicGo@main/img/1213309-20230703214038599-589042908.png" alt="img"></p>
<p><img src= "" data-lazy-src="https://cdn.jsdelivr.net/gh/cxz206015/For-PicGo@main/img/1213309-20230703214045012-103345244.png" alt="img"></p>
<p>但需要注意的是这项功能需要企业版或者 PRO 版本，简单来说无法白嫖。</p>
<h2 id="支持-next-项目与-Monorepos-仓库部署"><a href="#支持-next-项目与-Monorepos-仓库部署" class="headerlink" title="支持 next 项目与 Monorepos 仓库部署"></a>支持 next 项目与 Monorepos 仓库部署</h2><p>比如未来我们项目仓库都需要迁移 Monorepos，可能一个项目下需要分别部署单个子项目，目前 vercel 能很好支持 Monorepos 仓库部署，官方也提供了 Monorepos 仓库的例子，因为比较简单大家可以自行尝试。</p>
<h2 id="丰富的集成能力"><a href="#丰富的集成能力" class="headerlink" title="丰富的集成能力"></a>丰富的集成能力</h2><p>除了前面提到的 GitHub 在集成上表现，vercel 其实提供了配套的对于项目部署中性能检测、项目安全、项目监控、端到端测试等一系列能力，当然现在我们的做法是分散了不同平台（比如 sentry），而非自动化部署一条线中的一部分。</p>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta"><i class="fas fa-circle-user fa-fw"></i>文章作者: </span><span class="post-copyright-info"><a href="https://cxzde.cn">耶比大熊</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta"><i class="fas fa-square-arrow-up-right fa-fw"></i>文章链接: </span><span class="post-copyright-info"><a href="https://cxzde.cn/2024/04/09/note-vercel/">https://cxzde.cn/2024/04/09/note-vercel/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta"><i class="fas fa-circle-exclamation fa-fw"></i>版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://cxzde.cn" target="_blank">耶比大熊</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"></div><div class="post_share"><div class="social-share" data-image="https://tuchuang.voooe.cn/images/2024/04/07/background4.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1.1.3/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1.1.3/sharejs/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/2024/04/09/recommend/" title="推荐网站"><img class="cover" src= "" data-lazy-src="https://tuchuang.voooe.cn/images/2024/04/07/background8.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">推荐网站</div></div></a></div><div class="next-post pull-right"><a href="/2024/04/08/note-netty/" title="Netty"><img class="cover" src= "" data-lazy-src="https://tuchuang.voooe.cn/images/2024/04/07/background6.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">Netty</div></div></a></div></nav></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src= "" data-lazy-src="/img/avatar.jpg" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">耶比大熊</div><div class="author-info__description">隐约雷鸣 阴霾天空 但盼风雨来 能留你在此</div></div><div class="card-info-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">10</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">0</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">0</div></a></div><a id="card-info-btn" href="https://cxzde.cn"><i></i><span>🚀异次元之旅🚀</span></a><div class="card-info-social-icons is-center"><a class="social-icon" href="https://github.com/cxz206015" target="_blank" title="Github"><i class="fab fa-github" style="color: #24292e;"></i></a><a class="social-icon" href="mailto:2324308799@qq.com" target="_blank" title="Email"><i class="fas fa-envelope" style="color: #4a7dbe;"></i></a><a class="social-icon" href="https://www.zhihu.com/people/ashbell" target="_blank" title="知乎"><i class="iconfont icon-zhihu-circle-fill"></i></a></div></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn fa-shake"></i><span>公告</span></div><div class="announcement_content"><div style="text-align:center;"><a target="_blank" rel="noopener" href="https://www.bilibili.com/bangumi/play/ep785525/?share_source=copy_web">节选自——《凡人修仙传》86集 文戏<br></a></span>吾幼时好棋 <br>名镇乡野 <br>人皆谓神童 <br>年岁稍长 遇一山野老道 <br>三天对弈 终得一胜 <br>老道愿赌服输 施展法术 <br>通天遁地 <br>吾顿生神往 <br>遂弃棋修道 <br>可惜吾天赋一般 <br>只求矜矜业业 苦心钻研 <br>不惜远家人 抛爱侣 <br>弃友人 漂泊四海 无所依绊 <br>几经险境 <br>半生贫苦 <br>老来方有定所 <br>可惜 时日无多 <br>近来常思 <br>人生百年 蜉蝣一日 <br>长生于我何有哉 <br>不过又入樊笼尔 <br>不若二三好友 <br>弈棋饮酒 <br>良缘佳侣 人间携手 <br>风光百年 同归尘土 <br>但 <br>问道之心 <br>终归难改 <br>纵使蹉跎一生 <br>也要争那 一线天机 <br>只因幼时便知 <br>人生如棋 <br>落子 <br>无悔 <br></div></div><button></button></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%85%B3%E4%BA%8E-vercel"><span class="toc-number">1.</span> <span class="toc-text">关于 vercel</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Vercel-%E4%BC%98%E5%8A%BF%EF%BC%88%E8%83%BD%E5%B8%A6%E6%9D%A5%E4%BB%80%E4%B9%88%EF%BC%89"><span class="toc-number">2.</span> <span class="toc-text">Vercel 优势（能带来什么）</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E9%83%A8%E7%BD%B2%E6%96%B9%E5%BC%8F"><span class="toc-number">3.</span> <span class="toc-text">部署方式</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#GitHub%E9%83%A8%E7%BD%B2"><span class="toc-number">3.1.</span> <span class="toc-text">GitHub部署</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%9C%AC%E5%9C%B0%E9%83%A8%E7%BD%B2"><span class="toc-number">3.2.</span> <span class="toc-text">本地部署</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E6%9E%84%E5%BB%BA%E5%91%BD%E4%BB%A4%E4%B8%8E%E7%8E%AF%E5%A2%83%E4%BB%8B%E7%BB%8D"><span class="toc-number">4.</span> <span class="toc-text">构建命令与环境介绍</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E8%B8%A9%E5%9D%91%E4%B8%8E-vercel-%E5%B9%B3%E5%8F%B0%E8%AE%BE%E7%BD%AE"><span class="toc-number">5.</span> <span class="toc-text">踩坑与 vercel 平台设置</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E8%83%BD%E5%8A%9B%E8%BE%B9%E7%95%8C"><span class="toc-number">6.</span> <span class="toc-text">能力边界</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%94%AF%E6%8C%81%E5%9F%9F%E5%90%8D%E8%87%AA%E5%AE%9A%E4%B9%89"><span class="toc-number">6.1.</span> <span class="toc-text">支持域名自定义</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%94%AF%E6%8C%81%E9%83%A8%E7%BD%B2-URL-%E7%A7%81%E6%9C%89%E5%8C%96%E4%B8%8E%E9%A2%84%E8%A7%88%E5%9C%B0%E5%9D%80%E8%87%AA%E5%AE%9A%E4%B9%89%EF%BC%88%E8%A6%81%E9%92%B1%EF%BC%89"><span class="toc-number">6.2.</span> <span class="toc-text">支持部署 URL 私有化与预览地址自定义（要钱）</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%94%AF%E6%8C%81-next-%E9%A1%B9%E7%9B%AE%E4%B8%8E-Monorepos-%E4%BB%93%E5%BA%93%E9%83%A8%E7%BD%B2"><span class="toc-number">6.3.</span> <span class="toc-text">支持 next 项目与 Monorepos 仓库部署</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%B8%B0%E5%AF%8C%E7%9A%84%E9%9B%86%E6%88%90%E8%83%BD%E5%8A%9B"><span class="toc-number">6.4.</span> <span class="toc-text">丰富的集成能力</span></a></li></ol></li></ol></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2024 By 耶比大熊</div><div class="footer_custom_text">隐约雷鸣 阴霾天空 即使天无雨 我亦留此地<p><a target="_blank" href="https://hexo.io/" style="margin-inline:5px" title="博客框架为Hexo"><img src= "" data-lazy-src="https://img.shields.io/badge/Frame-Hexo-blue?style=flat&amp;logo=hexo" alt="博客框架为Hexo" ></a><a target="_blank" href="https://butterfly.js.org/" style="margin-inline:5px" title="博客主题为Butterfly"><img src= "" data-lazy-src="https://img.shields.io/badge/Theme-Butterfly-6513df?style=flat&amp;logo=bitdefender" alt="博客主题为Butterfly" ></a><a target="_blank" href="https://vercel.com/" style="margin-inline:5px" title="本站采用多线部署，主线路托管于Vercel"><img src= "" data-lazy-src="https://img.shields.io/badge/Hosted-Vercel-brightgreen?style=flat&amp;logo=Vercel" alt="本站采用多线部署，主线路托管于Vercel" ></a><a target="_blank" href="https://icp.gov.moe/?keyword=20245209" style="margin-inline:5px" title="本站已加入萌ICP豪华套餐，萌ICP备20245209号"><img src= "" data-lazy-src="https://img.shields.io/badge/%E8%90%8CICP%E5%A4%87-20245209-ff1485?style=flat&amp;logo=Bitwarden" alt="本站已加入萌ICP豪华套餐，萌ICP备20245209号" ></a><a target="_blank" href="https://github.com/" style="margin-inline:5px" title="本网站源码由Github提供存储仓库托管"><img src= "" data-lazy-src="https://img.shields.io/badge/Source-Github-d021d6?style=flat&amp;logo=GitHub" alt="本网站源码由Github提供存储仓库托管" ></a><a target="_blank" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" style="margin-inline:5px" title="本站采用知识共享署名-非商业性使用-相同方式共享4.0国际许可协议进行许可"><img src= "" data-lazy-src="https://img.shields.io/badge/Copyright-BY--NC--SA%204.0-d42328?style=flat&amp;logo=Claris" alt="本站采用知识共享署名-非商业性使用-相同方式共享4.0国际许可协议进行许可" ></a></p></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="translateLink" type="button" title="简繁转换">繁</button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside-config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><span class="scroll-percent"></span><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/js/utils.js?v=4.14.0-b1"></script><script src="/js/main.js?v=4.14.0-b1"></script><script src="/js/tw_cn.js?v=4.14.0-b1"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui@5.0.35/dist/fancybox/fancybox.umd.min.js"></script><script src="https://cdn.jsdelivr.net/npm/instant.page@5.2.0/instantpage.min.js" type="module"></script><script src="https://cdn.jsdelivr.net/npm/vanilla-lazyload@19.0.5/dist/lazyload.iife.min.js"></script><script src="https://cdn.jsdelivr.net/npm/node-snackbar@0.1.16/dist/snackbar.min.js"></script><script>(() => {
  const panguFn = () => {
    if (typeof pangu === 'object') pangu.autoSpacingPage()
    else {
      btf.getScript('https://cdn.jsdelivr.net/npm/pangu@4.0.7/dist/browser/pangu.min.js')
        .then(() => {
          pangu.autoSpacingPage()
        })
    }
  }

  const panguInit = () => {
    if (false){
      GLOBAL_CONFIG_SITE.isPost && panguFn()
    } else {
      panguFn()
    }
  }

  btf.addGlobalFn('pjaxComplete', panguInit, 'pangu')
  document.addEventListener('DOMContentLoaded', panguInit)
})()</script><div class="js-pjax"></div><script defer src="https://cdn.jsdelivr.net/gh/cxz206015/live2d-widget/autoload.js"></script><script id="canvas_nest" defer="defer" color="0,0,255" opacity="0.7" zIndex="-1" count="30" mobile="false" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1.1.3/dist/canvas-nest.min.js"></script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div id="local-search-results"></div><div id="local-search-stats-wrap"></div></div></div><div id="search-mask"></div><script src="/js/search/local-search.js?v=4.14.0-b1"></script></div></div></body></html>